package com.homedo.async.mysql.support;

import com.blinkfox.zealot.config.entity.NormalConfig;
import com.homedo.async.mysql.support.property.AsyncMysqlProperty;
import com.homedo.plainly.logger.Logger;
import com.homedo.plainly.logger.LoggerFactory;
import io.vertx.core.Vertx;
import io.vertx.mysqlclient.MySQLConnectOptions;
import io.vertx.mysqlclient.MySQLPool;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
 * @author jgshun
 */
@Configuration
@ComponentScan(basePackages = {
        "com.homedo.async.mysql.support"
})
public class ReactiveMysqlConfiguration {

    private static final Logger _LOGGER = LoggerFactory.getLogger(ReactiveMysqlConfiguration.class);

    @Bean
    NormalConfig normalZealotConfig(AsyncMysqlProperty asyncMysqlProperty) {
        return NormalConfig.getInstance() // 获取普通配置的唯一实例
                .setDebug(asyncMysqlProperty.isDebug())  // 设置是否开启debug模式，这样每次调用都会实时从最新的xml文件中获取sql，默认值为false.
                .setPrintBanner(false) // 设置是否打印zealot的启动banner，默认为true.
                .setPrintSqlInfo(asyncMysqlProperty.isPrintSqlInfo()); // 设置是否打印zealot的sql日志，默认为true.
    }


    @Bean(destroyMethod = "close")
    Pool createPool(@Autowired(required = false) Vertx vertx, AsyncMysqlProperty asyncMysqlProperty) {
        //情景：
//        1 服务启动并访问用户信息，接口正常。
//2 关闭机器网络，接口会一直处在连接中，而数据库查询语句也不会报错。
        MySQLConnectOptions connectOptions = new MySQLConnectOptions()
                .addProperty("useOldAliasMetadataBehavior", "yes")
                .addProperty("zeroDateTimeBehavior", "convertToNull")
                .addProperty("useUnicode", "true")
                .addProperty("characterEncoding", asyncMysqlProperty.getCharset())//UTF8
                .addProperty("application_name", asyncMysqlProperty.getAppName())
                .addProperty("_version", asyncMysqlProperty.getVersion())
                .addProperty("socketTimeout", "10000")
                .setConnectTimeout(asyncMysqlProperty.getConnectTimeout())//3000
                .setIdleTimeout(asyncMysqlProperty.getIdleTimeout())//1分钟 设置空闲超时，默认时间单位为秒。零表示不超时。 这确定连接是否超时并且如果在超时时间内未接收到数据则关闭连接
//                .setIdleTimeoutUnit(TimeUnit.MINUTES) 60
                .setReconnectInterval(asyncMysqlProperty.getReconnectInterval())//设置重新连接时间间隔 500
                .setReconnectAttempts(asyncMysqlProperty.getReconnectAttempts())//设置重新连接次数 1
                .setCharset(asyncMysqlProperty.getCharset())//UTF8
//                .setLogActivity(true)
                .setPort(asyncMysqlProperty.getPort())
                .setHost(asyncMysqlProperty.getHost())
                .setDatabase(asyncMysqlProperty.getDatabase())
                .setUser(asyncMysqlProperty.getUser())
                .setPassword(asyncMysqlProperty.getPassword())
                .setCachePreparedStatements(asyncMysqlProperty.isCachePreparedStatements())//true
                .setPreparedStatementCacheMaxSize(asyncMysqlProperty.getPreparedStatementCacheMaxSize())
                .setPreparedStatementCacheSqlLimit(asyncMysqlProperty.getPreparedStatementCacheSqlLimit());
        PoolOptions poolOptions = new PoolOptions().setMaxSize(asyncMysqlProperty.getPoolMaxSize())
                .setMaxWaitQueueSize(asyncMysqlProperty.getPoolMaxWaitQueueSize());//50 500
//
        MySQLPool client = null;
        if (vertx == null) {
            client = MySQLPool.pool(connectOptions, poolOptions);
        } else {
            client = MySQLPool.pool(vertx, connectOptions, poolOptions);
        }

        MySQLPool finalClient = client;

        Mono<Boolean> testConnMono = Mono.create(sink -> {
            finalClient.getConnection(res -> {
                if (res.succeeded()) {
                    sink.success(true);
                    res.result().close();
                } else {
                    sink.success(false);
                    _LOGGER.error("[连接数据库失败：{}]", res.cause().getMessage(), res.cause());
                }

            });
        });

        try {
            boolean isConn = testConnMono.block(Duration.ofSeconds(3));

            if (!isConn) {
                //这里用boolean值接收 是为了当程序启动错误时（数据库连接不上）可以手动停止程序。
                //返回null，是为了在启动失败立即退出
                vertx.close();
                return null;
            }
        } catch (Exception ex) {
            _LOGGER.error("[连接数据库超时：{}]", ex.getMessage(), ex);
            vertx.close();
            return null;
        }

        return client;
    }

}
